﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Reflection;
using System.Data.Linq.Mapping;
namespace Entity
{
    /// <summary>
    /// 实体基类，与linq to sql数据映射对应
    /// </summary>
    [Serializable]
    public abstract class EntityBase
    {

        public EntityBase()
        {
            this.IsRealDeleted = true;
        }
        #region Public Propertys 实体公用属性相关
        /// <summary>
        /// 实体主键
        /// 在子类中对它赋值，在其它类中可以访问到这个主键属性
        /// </summary>
        public abstract object[] PrimaryKey { get; }
        /// <summary>
        /// 是否执行真删除,默认为true,如果设为false，则更新实体的status字段
        /// </summary>
        public virtual bool IsRealDeleted { get; protected set; }
        /// <summary>
        /// 记录修改的列信息
        /// 子类可以根据需要，去复写记录数据的方式
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected virtual void PropertyChangedEvent(object sender, PropertyChangedEventArgs e)
        {
            #region 添加修改字段记录
            // VLog.IVLog log = new VLog.SqlVLog();
            // log.Write(string.Format("被修改的字段{0}", e.PropertyName));
            #endregion
            #region 记录修改的字段和修改成的值
            Type t = this.GetType();
            PropertyInfo pi = t.GetProperty(e.PropertyName);
            object value = pi.GetValue(this, null);
            this.OnPropertyChanged(e.PropertyName, value);
            #endregion

        }

        public EntityBase Log { get; set; }
        #endregion

        #region 实体验证

        /// <summary>
        /// 验证的字段名集合，为NULL表示验证所有字段
        /// </summary>
        public string[] ValidFields { get; set; }

        /// <summary>
        /// 数据验证(是否成功)
        /// 虚属性，子类可以根据自己的逻辑去复写
        /// </summary>
        public virtual bool IsValid { get { return this.GetRuleViolations().Count() == 0; } }
        /// <summary>
        /// 获取验证失败的信息枚举,默认提供了非空验证
        /// 它使用了简单的迭代器,如果GetRuleViolations有错误则返回迭代列表
        /// </summary> 
        /// <returns></returns>
        public virtual IEnumerable<RuleViolation> GetRuleViolations()
        {
            PropertyInfo[] propertyInfo = this.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
            if (ValidFields != null) propertyInfo = propertyInfo.Where(i => ValidFields.Contains(i.Name)).ToArray();
            foreach (var i in propertyInfo)
            {
                if (i.GetCustomAttributes(typeof(System.Data.Linq.Mapping.ColumnAttribute), false) != null
                    && i.GetCustomAttributes(typeof(System.Data.Linq.Mapping.ColumnAttribute), false).Count() > 0
                    && !((System.Data.Linq.Mapping.ColumnAttribute)i.GetCustomAttributes(typeof(System.Data.Linq.Mapping.ColumnAttribute), false)[0]).CanBeNull
                    && !((System.Data.Linq.Mapping.ColumnAttribute)i.GetCustomAttributes(typeof(System.Data.Linq.Mapping.ColumnAttribute), false)[0]).IsPrimaryKey)
                    if (i.GetValue(this, null) == null || string.IsNullOrEmpty(i.GetValue(this, null).ToString()))
                        yield return new RuleViolation("*", i.Name);
            }
        }
        #endregion

        #region 重写linq to sql的一些东西

        #region INotifyPropertyChanged and INotifyPropertyChanging Members

        public event PropertyChangedEventHandler BasePropertyChanged;
        public event PropertyChangingEventHandler BasePropertyChanging;
        protected virtual void OnPropertyChanging(String propertyName)
        {
            if ((this.BasePropertyChanging != null))
            {
                this.BasePropertyChanging(this, new PropertyChangingEventArgs(propertyName));
            }
        }
        protected virtual void OnPropertyChanged(String propertyName, object newValue)
        {
            if ((this.BasePropertyChanged != null))
            {
                this.BasePropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }

            if (_changeList == null)
                return;

            if (_changeList.ContainsKey(propertyName))
            {
                _changeList.Remove(propertyName);
            }
            _changeList.Add(propertyName, newValue);
        }
        protected bool IsPropertyChanged(string name)
        {
            return _changeList != null && _changeList.ContainsKey(name);
        }
        #endregion

        #region Change tracking

        private Dictionary<string, object> _changeList;

        public Dictionary<string, object> GetChanges()
        {
            return _changeList;
        }

        private void StartTrackChanges()
        {
            if (_changeList != null)
            {
                throw new InvalidOperationException("This object is already tracking changes");
            }
            _changeList = new Dictionary<string, object>();
        }

        private bool _IsAlreadySaved = false;

        public bool IsAlreadySaved()
        {
            return _IsAlreadySaved;
        }
        /// <summary>
        /// 保存实体
        /// </summary>
        public void MarkEntitySaved()
        {
            _IsAlreadySaved = true;
        }
        /// <summary>
        /// 实体初始化，开始跟踪实体的变化　
        /// </summary>
        public virtual void Initialization()
        {
            this.StartTrackChanges();
        }

        #endregion

        #endregion

        #region Events 一组实体修改相关事件
        /// <summary>
        /// 修改前
        /// </summary>
        public event Action ModifyBefore;
        /// <summary>
        /// 修改后
        /// </summary>
        public event Action<EntityBase> ModifyAfter;
        #endregion

        #region Public Methods 触发实体修改事件的方法
        public void OnModifyBefore()
        {
            if (ModifyBefore != null)
                this.ModifyBefore();
        }

        public void OnModifyAfter(EntityBase log)
        {
            if (ModifyAfter != null)
                this.ModifyAfter(log);
        }
        #endregion
    }
 
}
